package net.zoneland.zrdp.framework.config;

import java.util.concurrent.TimeUnit;

import lombok.Data;
import net.zoneland.zrdp.framework.interceptor.DatabaseLoggingInterceptor;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * HttpClient连接池初始化配置
 */
@Data
@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = "httpclient-pool")
public class HttpClientPoolConfig {

    /**
     * 最大连接数.
     */
    private int maxTotal = 200;

    /**
     * 每个域名主机的默认最大连接数. 默认0表示不设置.
     */
    private int defaultMaxPerRoute;

    /**
     * 客户端到服务端建立连接的超时时间.
     */
    private int connectTimeout = 5000;

    /**
     * 连接存活秒数.
     */
    private int connectionTimeToLiveSeconds = 0;

    /**
     * 客户端从服务器读取响应数据的超时时间. 注意此值过小会导致服务慢的请求没等数据返回就报错.
     */
    private int socketTimeout;

    /**
     * 连接保持活动的超时时间. 如果http header 中没有设置Keep-Alive超时时间，则使用此值.
     */
    private int keepAliveTimeout = 5000;

    /**
     * httpclient http代理，默认不设置时，jvm设置了http.proxyHost/http.proxyPort时还是会采用的.
     * 格式: IP:PORT或schema://IP:PORT
     */
    private String proxyHostPort;

    /**
     * 是否开启数据库日志.
     */
    private boolean dbLogEnabled;

    /**
     * 公共httpClient服务连接池
     * 注：Keep-Alive默认的实现会自动获取请求返回的Keep-Alive超时时间，如果http header 中没有这设置，则会使用配置的超时时间.
     * @return httpClient连接池服务对象
     */
    @Bean(name = "commonHttpClientPoolExecutor")
    public CloseableHttpClient commonHttpClientPoolExecutor(final HttpClientBuilder httpclientBuilder) {
        return httpclientBuilder.build();
    }

    //HttpClientBuilder也作为一个bean是为了有需要时可通过spring拦截，不用修改框架代码.
    @Bean
    public HttpClientBuilder httpclientBuilder() {
        // 配置连接池
        final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(maxTotal); // 最大连接数

        if (defaultMaxPerRoute > 0) {
            connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute); // 每个域名主机的默认最大连接数
        }

        // 配置代理 http 代理
        final HttpHost proxy;
        if (StringUtils.isEmpty(proxyHostPort)) {
            //set jvm http proxy if exists
            final String proxyHost = System.getProperty("http.proxyHost");
            if (StringUtils.isEmpty(proxyHost)) {
                proxy = null;
            } else {
                proxy = new HttpHost(proxyHost, Integer.getInteger("http.proxyPort", 8888));
            }
        } else {
            proxy = HttpHost.create(proxyHostPort);
        }
        // 配置请求
        final Builder configBuilder = RequestConfig.custom().setConnectTimeout(connectTimeout)
            .setSocketTimeout(socketTimeout).setProxy(proxy);
        final RequestConfig requestConfig = configBuilder.build();
        final HttpClientBuilder clientBuilder = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .setRedirectStrategy(LaxRedirectStrategy.INSTANCE) // 转发所有的请求
                .setDefaultRequestConfig(requestConfig)
                .setKeepAliveStrategy((response, context) -> {
                    // 调用默认策略
                    final long keepAlive = DefaultConnectionKeepAliveStrategy.INSTANCE.getKeepAliveDuration(response, context);
                    // 如果没有从响应中获取到keep-alive时间，则返回一个默认值
                    return keepAlive == -1 ? keepAliveTimeout : keepAlive;
                });

        if (dbLogEnabled) {
            clientBuilder.addInterceptorFirst((HttpRequestInterceptor) new DatabaseLoggingInterceptor()) // 请求拦截器
                .addInterceptorLast((HttpResponseInterceptor) new DatabaseLoggingInterceptor()); // 响应拦截器
        }
        if (connectionTimeToLiveSeconds > 0) {
            clientBuilder.setConnectionTimeToLive(connectionTimeToLiveSeconds, TimeUnit.SECONDS);
        }
        return clientBuilder;
    }
}
